Skip to content

Rebuild for plans-as-code (embedded TS evaluation) - #1

Merged
schickling merged 11 commits into
mainfrom
ts-rebuild
Jul 27, 2026
Merged

Rebuild for plans-as-code (embedded TS evaluation)#1
schickling merged 11 commits into
mainfrom
ts-rebuild

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Rebuilds Compass for the plans-as-code design settled in context/ (decisions 0011–0015): intent is authored as pure TypeScript, a version is the authored module stored unchanged and named by the hash of its source bytes, and reading a plan evaluates it in an embedded, capability-free engine.

What changed

  • Embedded evaluator (src/eval.rs): rquickjs (QuickJS-ng) + oxc, the crate's first external deps. Two-context bytecode dance — compile in a full context, evaluate in a locked one — so eval/new Function exist but are inert. Determinism allowlist (Math.random + transcendentals removed; no clock/fs/net/Intl). Bounded in time, memory, and stack.
  • compass prelude: the library plans import — plan/step/revise/reconcile/evidence (atom + all/any/not, vocabulary-free). A step's identity is its declared export name, recovered after evaluation and carried forward by revision.
  • Storage/authoring/CLI rewritten for source-byte identity; KDL and the draft machinery deleted. CLI: start · commit · show · history · ready · status · verify · repair · progress · evidence · version.
  • Examples fixed: added steps must be named exports to have identity (decision 0012) — an anonymous add: [step({...})] was a real defect in both the examples and 06-api/spec.md, found by building the thing. Fixing it changed the example bytes, hence new content-hash filenames.

Verification

  • cargo build --release, cargo clippy --all-targets -- -D warnings, cargo fmt --check — clean
  • cargo test — 129 lib + 7 acceptance
  • nix build .#compass — passes
  • Acceptance oracle: the committed example plans evaluate through the impl and reproduce their source-byte content-hash filenames
  • e2e: start → commit → show/history → revise → reconcile

Not in this PR

  • context/ VRS is unchanged except the 06-api example fix; the design was settled separately on main.
  • Fabric/replication wiring and the ready-queue remain out of scope per the roadmap.

Draft for review of the KDL→TS cutover before it lands on main.

schickling-assistant and others added 6 commits July 26, 2026 21:47
Add the plans-as-code evaluation core (decisions 0011/0012/0014): rquickjs
(QuickJS-ng) + oxc as the first external deps. Two-context bytecode dance
(compile in Context::full, evaluate in a locked Context::custom over
Json+Promise+Proxy), determinism allowlist (strip Math.random + transcendentals),
bounded evaluation (time/memory/stack). A compass prelude served for the bare
specifier provides plan/step/reconcile/evidence(atom+all/any/not via a
vocabulary-free Proxy). Step identity is the declared export name, attached by a
lazy host-injected footer and recovered after evaluation; revise/reconcile carry
steps forward by identity.

Root and revision modules evaluate and extract correctly; carry-forward proven.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorient the whole tool around evaluation (decisions 0012/0014):
- model: a Version is recovered by evaluating a module; identity is the sha256
  of source bytes; keep validate (cycle/dup/dep), changes_nothing, filenames.
- catalog: admission is byte-hash + filename check, no evaluation; parents are
  derived from import specifiers; source stored unchanged at 0444; evaluate()
  runs the module on demand.
- chain: head/divergence/orphan now walk import-derived parents.
- readiness: computed from an evaluated Version + events (per-head labelling
  moved to the CLI).
- predicate: add decision-0013 self-contradiction detection (refused at commit)
  and the write-time evidence cross-check (reported, never refused).
- CLI: start/commit/show/history/ready/status/verify/repair/progress/evidence/
  version, each with --json parity and a convergence line; reads render
  not-found/unresolved/stopped distinctly.
- refs: trimmed to event-id minting (plan/step identity is no longer minted).
- drop change.rs (structural-diff rendering) and the KDL @block use for versions.

Full e2e verified by hand: start -> edit -> commit -> show/history -> revise ->
divergence -> reconcile -> converged, all steps carried forward. 129 lib tests
pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…and VRS add-step defect

- tests/acceptance.rs: examples evaluate + filenames reproduce from source
  bytes; reconciliation carries both sides; full e2e lifecycle through the CLI
  (start -> commit -> show/history/ready -> revise -> diverge -> reconcile);
  and engine failure-mode guards (unresolved / stopped / sandbox: no eval, Date,
  Math.random).
- eval.rs: a missing imported predecessor now reads as Unresolved (wait), not a
  generic Failed — resolution succeeds so the loader reports the absent file.
  Drop the unused ctx threading through extract/to_pred.
- cmd.rs: the changes-nothing refusal is a plain string, not format!.
- context/06-api/spec.md: the 'A revision' example added a Step anonymously,
  which has no identity (decision 0012) — same defect the example plans had.
  Added steps are now named exports, so spec, prelude, and examples agree.
- example READMEs: point at the corrected version filenames.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- examples/README.md: the examples are evaluated (they are the acceptance
  suite), no longer 'not yet evaluated'.
- 02-artifacts DELTA-001: a distinct `repair` command now exists (refuses on a
  clean catalog, names the last intact predecessor); narrowed from open, with
  the remaining gap (author vs scaffold) recorded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The acceptance suite reads the committed example plans as fixtures, but the
fileset excluded examples/, so the tests passed locally (examples present in
the worktree) and failed under nix build (examples stripped from the sandbox
source). Only nix build catches this. Adding examples/ to the fileset.
A nix build output symlink whose blob content is a /nix/store path — not
something to carry in a public repo, and it churns on every build. Untrack
it and gitignore it.
@schickling
schickling marked this pull request as ready for review July 27, 2026 08:06
@schickling

Copy link
Copy Markdown
Contributor

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e824356c91

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/eval.rs Outdated
Comment thread src/cmd.rs
Comment thread src/prelude.js Outdated
schickling-assistant and others added 5 commits July 27, 2026 10:36
…dules

The ESM resolver accepted absolute and outside-the-catalog relative paths,
read them via SourceLoader, and evaluated them — handing a plan a filesystem
-read capability and breaking determinism, in violation of the capability
boundary (0011) and locked-evaluation intent (0014).

The resolver now permits only the bare `compass` prelude and content-addressed
plan-version modules: a same-plan sibling `./<seq>-<hash>.ts` or a cross-plan
`../../<plan>/versions/<seq>-<hash>.ts`. Paths are normalised lexically (never
against the real filesystem), so no `..` or symlink can redirect resolution
outside the catalog. Absolute paths, escaping references, and non-content
-addressed names are refused with a clear "illegitimate import" error. The
loader additionally enforces admission (content hash matches filename, per
02-artifacts) on every imported version, while sparing the entry module being
authored. Shared classification helpers (`normalize_lexical`,
`import_target_plan`) are exposed for the predecessor logic to reuse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Commit resolved every imported version-file against the current plan's store
and required each to be a committed predecessor, so a cross-plan reference
(`../../pl_other/versions/...`) failed to resolve and the module was wrongly
rejected as having an uncommitted predecessor — leaving the documented
cross-plan Step reference (CMP.API-R05) unusable.

An import is now a predecessor only when it is a version of the same plan; an
import of another plan's version is a cross-plan reference, resolved against
that other plan's store and required to be admitted there, but never made a
parent of the current version. Catalog admission applies the same rule when
deriving lineage, so a cross-plan reference no longer appears as an orphan
predecessor edge. Both paths classify through the shared import_target_plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When two reconciliation sides defined the same named Step with different
content, reconcile silently kept the first side and dropped the other — the
silent intent loss the whole reconciliation design exists to prevent.

Reconcile now detects when sides disagree on a same-identity Step (comparing
work, dependencies, acceptance, supersedes, and retirement) and refuses the
reconciliation unless it carries an explicit `edit` for that Step stating the
surviving intent. The error names the Step and both differing sides. A Step
only one side carries, or that both sides left identical, is not a conflict and
is carried forward unchanged. Resolves DQ08 in favour of never asserting intent
nobody wrote.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- imports_are_restricted_to_legitimate_modules: an absolute or catalog-escaping
  import is refused at eval; a valid same-plan sibling resolves.
- a_cross_plan_reference_is_not_a_predecessor: a cross-plan reference commits
  successfully, records no parent, and does not read as an orphan.
- reconciliation_refuses_divergent_same_step_without_an_explicit_edit: a
  divergent same-step reconcile is refused naming the step; an explicit edit
  resolves it; a non-conflicting reconcile still works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- decision 0016: a divergent same-Step reconciliation edit must be resolved
  explicitly (resolves DQ08), matching the never-silently-lose-intent principle.
- 06-api: the documented cross-plan dependsOn edge is not usable — validate()
  checks deps against the version's own steps and readiness folds within one
  plan. Narrowed CMP.API-R05 to the reference (which works), deferred the
  dependency edge to DQ11, and fixed the import path depth (../../, not ../).
- 05-integrations: refined CMP.INT-R06 — the substrate cannot express
  completeness any more than the catalog can, so convergence is at most
  'quiescent as of now', never 'you have everything'.
- DQ12: variable-in-content vs relative-import+env-root machine-agnosticism.
- fix the repair delta to the enforced shape (status open, bare direction).
@schickling
schickling merged commit 67014b3 into main Jul 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants